
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Spinning.Spinning.Interface;
using Spinning.Spinning.Unitity;
using Spinning.Spinning.EntityModifiers;
using Microsoft.Xna.Framework.Graphics;

namespace Spinning.Spinning.Entity
{
    public abstract class BaseEntity : IEntity
    {
        static EntityComparer _entityComparer;
        public delegate void UpdateHandler(GameTime gameTime);
        public UpdateHandler UpdateHandlerListener;
        public List<IEntity> Children { get; protected set; }
        public List<IUpdateHandler> UpdateHandlers { get; protected set; }

        public bool Visable { get; set; }


        public BaseEntity()
        {
            Children = new List<IEntity>();
            UpdateHandlers = new List<IUpdateHandler>();
            _motifiers = new List<EntityModifier>();
            Color = Microsoft.Xna.Framework.Color.White;
            Scale = new Microsoft.Xna.Framework.Vector2(1);
            Alpha = 1;
            Visable = true;
            Static = false;
            if (_entityComparer == null)
                _entityComparer = new EntityComparer();
        }



        List<EntityModifier> _motifiers;

        public virtual void OnLoad()
        {

        }

        public void OnDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Microsoft.Xna.Framework.GameTime gameTime)
        {
            if (Visable)
            {
                DrawSelf(spriteBatch, gameTime);
                drawChildren(spriteBatch, gameTime);
            }
        }

        protected virtual void DrawSelf(SpriteBatch spriteBatch,GameTime gameTime) { }

        public void OnUpdate(GameTime gameTime)
        {
            if (Visable)
            {
                if (UpdateHandlerListener != null)
                    UpdateHandlerListener(gameTime);
                if (Static)
                {
                    Projection = Matrix.Identity;
                    UnProjection = Matrix.Identity;
                }
                else
                {
                    UpdateProjection(gameTime);
                    UpdateUnProjection(gameTime);
                }
                updateChildren(gameTime);
                updateMotifiers(gameTime);
                updateHandlers(gameTime);
                UpdateSelf(gameTime);
            }
        }



        protected virtual void UpdateProjection(GameTime gameTime)
        {
            Matrix m = Matrix.Identity;//screen

            if (Parent != null)
            {
                m = Parent.GetProjectionMatrix();
            }
            Projection = m;
        }

        protected virtual void UpdateUnProjection(GameTime gameTime)
        {
            Matrix m = Matrix.Identity;//screen

            if (Parent != null)
            {
                m = Parent.GetUnProjectionMatrix();
            }
            UnProjection = m;
        }

        protected virtual void UpdateSelf(GameTime gameTime) { }

        public void Attach(IEntity entity)
        {
            Children.Add(entity);
            entity.Parent = this;
        }

        public void Detach(IEntity entity)
        {
            Children.Remove(entity);
            entity.Parent = null;
        }

        public virtual Microsoft.Xna.Framework.Vector2 Position { get; set; }

        public Microsoft.Xna.Framework.Vector2 Origin { get; set; }

        public Microsoft.Xna.Framework.Vector2 Scale { get; set; }

        public float ZIndex { get; set; }

        public virtual float Rotation { get; set; }

        public Microsoft.Xna.Framework.Color Color { get; set; }

        public float Alpha
        {
            get { return ((float)Color.A) / 255f; }
            set
            {
                Color = new Microsoft.Xna.Framework.Color(new Vector4(value));
            }
        }

        protected virtual void updateChildren(Microsoft.Xna.Framework.GameTime gameTime)
        {
            foreach (IEntity e in Children)
                e.OnUpdate(gameTime);
        }

        protected void updateMotifiers(GameTime gameTime)
        {
            foreach (EntityModifier m in _motifiers)
                m.OnUpdate(this, gameTime);
        }

        protected void updateHandlers(GameTime gameTime)
        {
            foreach (IUpdateHandler m in UpdateHandlers)
                m.OnUpdate(gameTime);
        }

        protected virtual void drawChildren(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Microsoft.Xna.Framework.GameTime gameTime)
        {
            foreach (IEntity e in Children)
                e.OnDraw(spriteBatch, gameTime);
        }

        public void SortChildren()
        {
            Children.Sort(_entityComparer);
        }

        public void AddModifier(EntityModifier modifier)
        {
            _motifiers.Add(modifier);
        }

        public void RemoveEntityModifier(EntityModifier modifier)
        {
            _motifiers.Remove(modifier);
        }

        public IEntity Parent { get; set; }


        public Matrix Projection
        {
            get;
            protected set;
        }


        protected Matrix ProjectionMatrix(IEntity entity)
        {
            Vector2 entityPos = entity.Position;
            Matrix translateBackToPosition = Matrix.CreateTranslation(entityPos.X, entityPos.Y, 0);

            Matrix compositeMatrix = translateBackToPosition;
            return compositeMatrix;
        }

        protected virtual Matrix UnProjectionMatrix(IEntity entity)
        {
            Vector2 entityPos = entity.Position;
            Matrix translateBackToPosition = Matrix.CreateTranslation(-entityPos.X, -entityPos.Y, 0);

            Matrix compositeMatrix = translateBackToPosition;
            return compositeMatrix;
        }

        public virtual Matrix GetProjectionMatrix()
        {
            return ProjectionMatrix(this) * this.Projection;
        }


        public Matrix UnProjection
        {
            get;
            protected set;
        }
        public bool Static { get; set; }
        public virtual Matrix GetUnProjectionMatrix()
        {
            return this.UnProjection * UnProjectionMatrix(this);
        }
    }
}
